////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.transport
{

import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.net.LocalConnection;
import flash.utils.getClassName;

import ru.goodcore.errors.GenericError;
import ru.goodcore.errors.GenericErrorCode;
import ru.goodcore.net.URI;

public class LocalConnectionTransport extends AbstractTransport {

	//-------------------------------------------------------------------------
	//
	//   Constructor
	//
	//-------------------------------------------------------------------------

	public function LocalConnectionTransport() {
		this._lc = new LocalConnection();
		this._lc.allowDomain('*', 'localhost');
		this._lc.client = {
							command: this.handler_command,
							handle: this.handler_command
						};
	}

	//-------------------------------------------------------------------------
	//
	//   Private variables
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 */
	private var _lc:			LocalConnection;

	/**
	 * @private
	 */
	private var _localPort:		String;

	/**
	 * @private
	 */
	private var _remotePort:	String;



	//-------------------------------------------------------------------------
	//
	//   Overriden public methods
	//
	//-------------------------------------------------------------------------

	public override function connect(uri:URI):void {
		this._remotePort = uri.nonHierarchical;
		this._localPort = '_LCa' + uint(Math.random() * int.MAX_VALUE).toString();

		this._lc.addEventListener(StatusEvent.STATUS,					this.handler_statusConnect);
		this._lc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_errorConnect);

		try {
			this._lc.connect(this._localPort);
		}
		catch (error:Error) {
			trace(getClassName(this) + ' cant connect to ' + this._localPort);
			this._lc.removeEventListener(StatusEvent.STATUS,					this.handler_statusConnect);
			this._lc.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_errorConnect);
			super.dispatchEvent( new IOErrorEvent(IOErrorEvent.IO_ERROR) );
			return;
		}

		this._lc.send(this._remotePort, 'init', this._localPort);
	}


	public override function send(options:Object = null):void {
		if ( !super.connected)
			throw new GenericError(GenericErrorCode.INVALID_SOCKET);

		var args:	Array = new Array();

		if (!options) {
			options = new Array();
		}

		if (options is Array) {
			args = options as Array;
		} else {
			args.push(options);
		}

		args.unshift(this._remotePort, 'handle');
		this._lc.send.apply(this, args);
	}


	public override function close():void {
		super.connected = false;

		try {
			this._lc.close();
		} catch (error:Error) {
			// ignore
		}

	}


	//-------------------------------------------------------------------------
	//
	//   Event handlers
	//
	//-------------------------------------------------------------------------

	/**
	 * @private 
	 */
	private function handler_statusConnect(event:StatusEvent):void {
		trace(getClassName(this) + ' handler_statusConnect: ' + event);

		this._lc.removeEventListener(StatusEvent.STATUS,				this.handler_statusConnect);
		this._lc.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_errorConnect);

		switch (event.level) {
			case 'status':
				this._lc.addEventListener(StatusEvent.STATUS,					this.handler_statusSend);
				this._lc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_errorSend);

				super.connected = true;
				super.dispatchEvent( new Event(Event.CONNECT) );
				break;

			case 'error': 
			default:
				super.dispatchEvent( new IOErrorEvent(IOErrorEvent.IO_ERROR) );
		}

	}

	/**
	 * @private 
	 */
	private function handler_errorConnect(event:SecurityErrorEvent):void {
		trace(getClassName(this) + ' handler_errorConnect: ' + event);
		this._lc.removeEventListener(StatusEvent.STATUS,				this.handler_statusConnect);
		this._lc.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_errorConnect);
		super.dispatchEvent( new IOErrorEvent(event.type) );
	}




	/**
	 * @private 
	 */
	private function handler_statusSend(event:StatusEvent):void {
		trace(getClassName(this) + ' handler_statusSend: ' + event);

		switch (event.level) {
			case 'error':
				this._lc.removeEventListener(StatusEvent.STATUS,				this.handler_statusSend);
				this._lc.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_errorSend);
				super.connected = false;
				//super.dispatchEvent( new IOErrorEvent(IOErrorEvent.IO_ERROR) );
				super.dispatchEvent( new Event(Event.CLOSE) );
		}
	}

	/**
	 * @private 
	 */
	private function handler_errorSend(event:SecurityErrorEvent):void {
		trace(getClassName(this) + ' handler_errorSend: ' + event);
		//super.dispatchEvent( new IOErrorEvent(IOErrorEvent.IO_ERROR) );
		super.dispatchEvent( new Event(Event.CLOSE) );
	}




	/**
	 * @private 
	 */
	private function handler_command(data:Object):void {
		trace(getClassName(this) + ' message: ' + data.toString());
		super.data = data;
		super.dispatchEvent( new ProgressEvent(ProgressEvent.SOCKET_DATA) );
	}

}
}